// wget.cs
// ------------------------------------------------------------------
//
// Utility to perform an http get from the command line.
//
// Author: Dino
// Created Thu Jul 02 10:32:50 2009
//
// last saved: <2011-October-14 21:10:56>
// ------------------------------------------------------------------
//
// Copyright (c) 2011 by Dino Chiesa
// All rights reserved!
//
// Licensed under the MS-PL.  See the accompanying License.txt file for
// details.
//
// ------------------------------------------------------------------

using System;
using System.Diagnostics;
using System.IO;
using System.Net;


namespace Ionic.Toys
{
    public class Wget
    {
        enum AppAction
        {
            None,
            Run
        }

        private AppAction _action = AppAction.None;
        private string _url;
        private bool _quiet;
        private string _destfile;
        private System.IO.Stream _dest;
        private System.Int64 _contentLength;
        private System.String _contentType;

        // ctor
        public Wget () {}

        // ctor
        public Wget (string[] args)
        {
            for (int i=0; i < args.Length; i++)
            {
                switch (args[i])
                {
                    case "-?":
                    case "--help":
                        Usage();
                        return;

                    case "-q":
                        _quiet = true;
                        break;

                    default:
                        if (_url == null)
                        {
                            _url = args[i];
                        }
                        else if (_destfile == null)
                        {
                            _destfile = args[i];
                        }
                        else
                            throw new ArgumentException(args[i]);
                        break;
                }
            }

            if (_url == null)
            {
                Usage();
                return;
            }
            _action = AppAction.Run;


            // set up the destination
            if (_destfile == "-")
            {
                if(!_quiet)
                    Console.WriteLine("  ==> (stdout)");
                _dest = Console.OpenStandardOutput();
            }
            else
            {
                if (_destfile == null)
                {
                    var bldr = new System.UriBuilder(_url);
                    int ix = bldr.Path.LastIndexOf('/');
                    if (ix+1 < bldr.Path.Length)
                    {
                        _destfile = bldr.Path.Substring(ix+1);
                    }

                }
                if (_destfile == null)
                {
                    _dest = OpenUniqueFile();
                }
                else
                {
                if(!_quiet)
                    Console.WriteLine("  ==> {0}", _destfile);
                _dest = File.Create(_destfile);
                }
            }
        }



        private Stream OpenUniqueFile()
        {
            string uniqueName =
                Path.Combine(".",
                             String.Format("wget.{0}.out", Path.GetRandomFileName()));
            if(!_quiet)
                Console.WriteLine("  ==> {0}", uniqueName);
            return File.Create(uniqueName);
        }


        private void Pump(Stream input, Stream output)
        {
            Int64 totalTransferred = 0;
            int lastPct = 0;
            byte[] bytes = new byte[4096];
            int n;

            int left= 0, top= 0;
            if (!_quiet)
            {
                left = Console.CursorLeft;
                top= Console.CursorTop;
            }

            while((n = input.Read(bytes, 0, bytes.Length)) != 0)
            {
                output.Write(bytes, 0, n);
                if (!_quiet)
                {
                    if (_contentLength > 0)
                    {
                        if (lastPct == 0)
                        {
                            Console.SetCursorPosition(left, top);
                            Console.Write("{0,5:N1}% [{1}]", 0, new String(' ', 50));
                        }
                        totalTransferred += n;
                        var pct = (100.0 * totalTransferred) / _contentLength ;
                        Console.SetCursorPosition(left, top);
                        Console.Write("{0,5:N1}% [", pct);
                        if ((int)(pct/2) > lastPct)
                        {
                            lastPct = (int) (pct/2);
                            Console.Write("{0}", new String('=', lastPct));
                        }
                    }
                }
            }
            if (!_quiet)
                Console.WriteLine("] {0:N0}", totalTransferred );
        }


        private void HttpGet()
        {
            var timer = new Stopwatch();
            timer.Start();
            using (WebClient client = new WebClient ())
            {
                using (var s = client.OpenRead(_url))
                {
                    if (!_quiet)
                    {
                        WebHeaderCollection headers = client.ResponseHeaders;
                        var cl = headers["Content-Length"];
                        if (cl!=null)
                        {
                            _contentLength = System.Int64.Parse(cl);
                        }
                        _contentType =  headers["Content-Type"];

                        if (_contentLength > 0 && _contentType != null)
                        {
                            Console.WriteLine ("  {0:N0} bytes [{1}]", _contentLength, _contentType);
                        }
                    }

                    Pump(s, _dest);
                }
            }
            timer.Stop();
            TimeSpan ts = timer.Elapsed;
            if (!_quiet)
            {
                if (_contentLength > 0)
                    Console.WriteLine("  {0:N1}s ({1:N1}kb/s)",
                                      ts.TotalSeconds, (_contentLength / 1024.0) / ts.TotalSeconds);
                else
                    Console.WriteLine("  {0:N1}s", ts.TotalSeconds);
            }
        }


        private void Run()
        {
            if (_action != AppAction.Run) return;
            HttpGet();
        }

        public static void Usage()
        {
            Console.WriteLine("\nwget: download content from a URL via HTTP GET.\n");
            Console.WriteLine("Usage:\n  wget <url> [<localfile>]");
        }


        public static void Main(string[] args)
        {
            try
            {
                Wget me = new Wget(args);
                me.Run();
            }
            catch (System.Exception exc1)
            {
                Console.WriteLine("Exception: {0}", exc1.ToString());
            }
        }
    }
}
